#!/usr/bin/env python
# -*- coding: UTF-8 -*-

from oriolus.pith.packets.packet import FullName

class InstallRequirements:
    def check(self, repository, packet, **conditions):
        if repository.find_packet_by_fullname(packet.name):
            return False, "Packet %s already exists" % (packet.name)

        implicit_conflicts = self.__find_implicit_conflicts(packet, repository)
        if implicit_conflicts:
            implicit_conflict_list = [conflict.name for conflict in implicit_conflicts]
            return False, "Installed packets %s are conflicting with packet %s" % (", ".join(implicit_conflict_list), packet.name)

        implicit_replaces = self.__find_implicit_replaces(packet, repository)
        if implicit_replaces:
            implicit_replace_list = [replace.name for replace in implicit_replaces]
            return False, "Installed packets %s require to replace packet %s" % (", ".join(implicit_replace_list), packet.name)

        uninstalled_depends = self.__find_uninstalled_dependencies(packet, repository)
        if uninstalled_depends:
            dependency_list = [str(FullName(dependency["Name"], dependency["Version"])) for dependency in uninstalled_depends]
            return False, "Packet %s requires dependencies %s to be installed" % (packet.name, ", ".join(dependency_list))

        installed_replaces = self.__find_installed_replaces(packet, repository)
        if installed_replaces:
            replace_list = [replace.name for replace in installed_replaces]
            return False, "Packet %s requires to replace %s" % (packet.name, ", ".join(replace_list))

        installed_conflicts = self.__find_installed_conflicts(packet, repository)
        if installed_conflicts:
            conflict_list = [conflict.name for conflict in installed_conflicts]
            return False, "Packet %s conflicts with %s" % (packet.name, ", ".join(conflict_list))

        return True, ""

    def __find_implicit_conflicts(self, packet, repository):
        return [each for each in repository.get_all() if each.meta.is_in_conflicts(packet.meta.name, packet.meta.version)]

    def __find_implicit_replaces(self, packet, repository):
        return [each for each in repository.get_all() if each.meta.is_in_replaces(packet.meta.name, packet.meta.version)]

    def __find_uninstalled_dependencies(self, packet, repository):
        dependencies = packet.meta.depends
        action = packet.meta.is_in_depends
        
        return self.__find_uninstalled_packets(dependencies, action, repository)

    def __find_installed_replaces(self, packet, repository):
        replaces = packet.meta.replaces
        action = packet.meta.is_in_replaces
        
        return self.__find_installed_packets(replaces, action, repository)
    
    def __find_installed_conflicts(self, packet, repository):
        conficts = packet.meta.conflicts
        action = packet.meta.is_in_conflicts
        
        return self.__find_installed_packets(conficts, action, repository)
    
    def __find_uninstalled_packets(self, list, action, repository):
        result = []

        for item in list:
            item_name = item["Name"]
            uninstalled = item_name
            packets = repository.find_packets_by_name(item_name)
            for packet in packets:
                if action(packet.meta.name, packet.meta.version):                    
                    uninstalled = None
                    break
            if uninstalled is not None:
                result.append(item)

        return result
    
    def __find_installed_packets(self, list, action, repository):
        result = []

        for item in list:
            item_name = item["Name"]
            packets = repository.find_packets_by_name(item_name)
            for packet in packets:
                if action(packet.meta.name, packet.meta.version):
                    result.append(packet)

        return result

class RemoveRequirements:
    def check(self, repository, packet_name, **conditions):
        if not repository.find_packet_by_fullname(packet_name):
            return False, RemoveErrorReason(RemoveErrorType.NoPacket, packet_name, 
                                            "Packet %s couldn't be found" % packet_name, None)

        installed_children = repository.find_child_packets(packet_name)
        obstructive_children = self.__select_obstructive_children(installed_children, packet_name, repository)
        if obstructive_children:
            child_list = [FullName.parse(child.name) for child in obstructive_children]
            return False, RemoveErrorReason(RemoveErrorType.HasChildPackets, packet_name, 
                                            "Packets [%s] must be deleted first" % ", ".join(map(str, child_list)),
                                            child_list)

        return True, ""

    def __select_obstructive_children(self, installed_children, packet_name, repository):
        obstructive_children = []

        for child in installed_children:
            actual_dependencies = []
            for dependency in child.meta.depends:
                dependency_name = dependency["Name"]

                if dependency_name != FullName.parse(packet_name).name:
                    continue

                packets = repository.find_packets_by_name(dependency_name)
                for packet in packets:
                    if child.meta.is_in_depends(packet.meta.name, packet.meta.version):
                        actual_dependencies.append(packet)

            if len(actual_dependencies) == 1:
                obstructive_children.append(child)

        return obstructive_children

class RemoveErrorReason:
    def __init__(self, type, packet_name, message, data):
        self.type = type
        self.packet_name = packet_name
        self.message = message
        self.data = data

class RemoveErrorType:
    NoPacket = "no packet"
    HasChildPackets = "has child packets" 